﻿//     Copyright (c) Microsoft Corporation.  All rights reserved.

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Web.Mvc;
using Gateway.Console.Models;
using Gateway.Console.Services;

namespace Gateway.Console.Controllers
{
    public class OAuthAccessController : Controller
    {
        protected ConsoleContext _db = new ConsoleContext();
        private string _controllerName;
        private ResourceSelectors[] _defaultAccessSelectors;

        protected OAuthAccessController(string controllerName, ResourceSelectors[] defaultAccessSelectors)
        {
            _controllerName = controllerName;
            _defaultAccessSelectors = defaultAccessSelectors;
        }

        protected ActionResult DoWithAccess(string action,
            Action<ResourceSelectors, string, string> accessFailure,
            Func<ActionResult> controllerAction)

        {
            return DoWithAccessAsync(action, accessFailure, () => Task.Run(controllerAction)).Result;
        }

        protected async Task<ActionResult> DoWithAccessAsync(string action,
            Action<ResourceSelectors, string, string> accessFailure,
            Func<Task<ActionResult>> controllerAction)
        {
            return await DoWithAccessAsync(this._defaultAccessSelectors, action, accessFailure, controllerAction);
        }

        protected async Task<ActionResult> DoWithAccessAsync(IEnumerable<ResourceSelectors> accessSelectors, 
            string action,
            Action<ResourceSelectors, string, string> accessFailure,
            Func<Task<ActionResult>> controllerAction)
        {
            ViewBag.RdfeAuthorizationFailure = false;
            ViewBag.GraphApiAuthorizationFailure = false;
            foreach (var accessSelector in accessSelectors)
            {
                if (Session.HasAuthorizationCodeFailed(accessSelector))
                {
                    string errorCode;
                    string errorDescription;
                    Session.GetAuthorizationCodeFailureDetails(accessSelector, out errorCode, out errorDescription);
                    if (Session.IsProvisioningPermission(accessSelector) && String.Equals(errorCode, "access_denied", StringComparison.OrdinalIgnoreCase))
                    {
                        await Task.Delay(1000);
                        return Session.GetAuthorizationCode(accessSelector, _controllerName, action);
                    }
                    else
                    {
                        ViewBag.AuthorizationErrorCode = errorCode;
                        ViewBag.AuthorizationErrorDescription = errorDescription;
                        if (accessFailure == null)
                        {
                            DefaultAccessFailureHandler(accessSelector, errorCode, errorDescription);
                        }
                        else
                        {
                            accessFailure(accessSelector, errorCode, errorDescription);
                        }
                    }
                }
                else if (!Session.HasAccessToken(accessSelector))
                {
                    return Session.GetAuthorizationCode(accessSelector, _controllerName, action);
                }
            }
            return await controllerAction();
        }

        protected void DefaultAccessFailureHandler(ResourceSelectors selector, string errorCode, string errorDescription)
        {
            switch (selector)
            {
                case ResourceSelectors.RDFE:
                    ViewBag.RdfeAuthorizationFailure = true;
                    break;

                case ResourceSelectors.GraphApi:
                    ViewBag.GraphApiAuthorizationFailure = true;
                    break;
            }
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (_db != null)
                {
                    _db.Dispose();
                }
            }
            base.Dispose(disposing);
        }
    }
}